#pragma once

#include "data.hpp"
#include <functional>
#include <map>
#include <optional>

#define TYPE_U64     "u64"
#define TYPE_U32     "u32"
#define TYPE_U16     "u16"
#define TYPE_U8      "u8"
#define TYPE_I64     "i64"
#define TYPE_I32     "i32"
#define TYPE_I16     "i16"
#define TYPE_I8      "i8"
#define TYPE_BITS    "bits"
#define TYPE_CLASS   "class"
#define TYPE_STR     "str"
#define TYPE_FLOAT   "f32"
#define TYPE_DOUBLE  "f64"
#define TYPE_BOOL    "bool"
#define TYPE_STR_HEX "str_hex"

class PointTable_t;

struct Point_t {
public:
    using PointOperator_t = std::function<void(void *, const std::string &, OldData_t &, NewData_t &)>;

    uint16_t addr;
    uint16_t len;
    uint8_t readFunc;
    uint8_t writeFunc;
    std::optional<double> unit;
    std::string type;
    std::shared_ptr<PointTable_t> pointTable;
    Data rData;
    std::optional<std::pair<uint32_t, uint32_t>> offset;
    std::optional<PointOperator_t> pointOperatorCallback;
    std::chrono::time_point<std::chrono::high_resolution_clock> time;

private:
    bool operatorResult;

public:
    Point_t();
    Point_t(uint16_t a, uint16_t l, uint8_t r, uint8_t w, const std::shared_ptr<PointTable_t> &p, const std::optional<std::pair<uint32_t, uint32_t>> &o);

    void opSuccess(void);
    void opFailed(void);
    bool opResult(void);

    bool isFatherPoint(void);
};

Point_t::Point_t()
{
    addr = len = readFunc = writeFunc = 0;
    pointTable = nullptr;
    offset = std::make_pair(0, 0);
    time = std::chrono::high_resolution_clock::time_point(std::chrono::seconds(0));
    operatorResult = false;
}

Point_t::Point_t(uint16_t a, uint16_t l, uint8_t r, uint8_t w, const std::shared_ptr<PointTable_t> &p, const std::optional<std::pair<uint32_t, uint32_t>> &o)
{
    addr = a;
    len = l;
    readFunc = r;
    writeFunc = w;
    pointTable = p;
    offset = o;
}

void Point_t::opSuccess(void)
{
    operatorResult = true;
}

void Point_t::opFailed(void)
{
    operatorResult = false;
}

bool Point_t::opResult(void)
{
    return operatorResult;
}

bool Point_t::isFatherPoint(void)
{
    return pointTable != nullptr;
}

class PointTable_t : public std::map<std::string, Point_t> {
private:
    std::optional<Point_t *> _findPoint(const std::string &name, PointTable_t &pt);
    std::optional<Point_t *> _findReadPoint(const std::string &name, PointTable_t &pt); /* 找要读的点 */

public:
    std::optional<Point_t *> findPoint(const std::string &name);                         /* 找指定的点 */
    std::optional<Point_t *> findReadPoint(const std::string &name);                     /* 找要读的点 */
    std::optional<std::pair<const std::string, Point_t> *> findFirstReadAblePoint(void); /* 找到第一个可读的点 */

    PointTable_t() = default;
    PointTable_t(const std::initializer_list<const std::pair<const std::string, Point_t>> &list);
};

PointTable_t::PointTable_t(const std::initializer_list<const std::pair<const std::string, Point_t>> &list)
{
    for (const auto &item : list) {
        insert(item);
    }
}

std::optional<Point_t *> PointTable_t::_findPoint(const std::string &name, PointTable_t &pt)
{
    for (auto &[key, value] : pt) {
        if (key == name) {
            return &value;
        }

        if (value.pointTable) {
            if (auto ret = _findPoint(name, *value.pointTable); ret.has_value()) {
                return ret;
                // return &value; /* 返回父级节点 */
            }
        }
    }

    return std::nullopt;
}

std::optional<Point_t *> PointTable_t::_findReadPoint(const std::string &name, PointTable_t &pt)
{
    for (auto &[key, value] : pt) {
        if (key == name) {
            return &value;
        }

        if (value.pointTable) {
            if (auto ret = _findPoint(name, *value.pointTable); ret.has_value()) {
                return &value; /* 返回父级节点 */
            }
        }
    }

    return std::nullopt;
}

std::optional<Point_t *> PointTable_t::findPoint(const std::string &name)
{
    return _findPoint(name, *this);
}

std::optional<Point_t *> PointTable_t::findReadPoint(const std::string &name)
{
    return _findReadPoint(name, *this);
}

std::optional<std::pair<const std::string, Point_t> *> PointTable_t::findFirstReadAblePoint(void)
{
    for (auto &item : *this) {
        if (item.second.readFunc) {
            return &item;
        }
    }

    return std::nullopt;
}

void addPointOperationCallback(Point_t &point, const Point_t::PointOperator_t &op)
{
    if (point.readFunc && point.type != TYPE_CLASS) {
        point.pointOperatorCallback = op;
    }

    if (point.pointTable) {
        for (auto &p : *point.pointTable) {
            addPointOperationCallback(p.second, op);
        }
    }
}
